/*
Copyright 2016, Intel Corporation.

The source code, information and material("Material") contained herein is
owned by Intel Corporation or its suppliers or licensors, and title to
such Material remains with Intel Corporation or its suppliers or licensors.
The Material contains proprietary information of Intel or its suppliers and
licensors. The Material is protected by worldwide copyright laws and treaty
provisions.No part of the Material may be used, copied, reproduced,
modified, published, uploaded, posted, transmitted, distributed or
disclosed in any way without Intel's prior express written permission. No
license under any patent, copyright or other intellectual property rights
in the Material is granted to or conferred upon you, either expressly, by
implication, inducement, estoppel or otherwise. Any license under such
intellectual property rights must be express and approved by Intel in
writing.

Unless otherwise agreed by Intel in writing, you may not remove or alter
this notice or any other notice embedded in Materials by Intel or Intel's
suppliers or licensors in any way.
*/

#include "stdafx.h"

#define ENCLAVEBRIDGE_API_EXPORTING 1

#include "EnclaveBridge.h"
#include "sgx_urts.h"
#include <stdio.h>
#include <iostream>
#include <tchar.h>
#include "sgx_uae_service.h"
#include "PasswordManagerError.h"
#include "Enclave_u.h"

#define ENCLAVE_FILE _T("Enclave.signed.dll")
//#define MAX_BUF_LEN 100

static int get_enclave(sgx_enclave_id_t *eid);
static int create_enclave(sgx_enclave_id_t *eid);
static int lost_enclave();

static sgx_enclave_id_t enclaveId = 0;
static sgx_launch_token_t launch_token = { 0 };
static int updated= 0;
static int launched = 0;
static sgx_status_t sgx_status= SGX_SUCCESS;


#define RETURN_SGXERROR_OR(X) if(sgx_status == SGX_SUCCESS) return X; else if (lost_enclave()) return NL_STATUS_LOSTENCLAVE; else return NL_STATUS_SGXERROR;

// Ensure the enclave has been created/launched.

static int get_enclave(sgx_enclave_id_t *eid)
{
	if (launched) return 1;
	else return create_enclave(eid);
}

static int create_enclave(sgx_enclave_id_t *eid)
{
	sgx_status = sgx_create_enclave(ENCLAVE_FILE, SGX_DEBUG_FLAG, &launch_token, &updated, &enclaveId, NULL);
	if (sgx_status == SGX_SUCCESS) {
		if ( eid != NULL ) *eid = enclaveId;
		launched = 1;
		return 1;
	}

	return 0;
}

// If we lose the enclave, we lose our data. Technically we should be able to recover from this.
// Recovery functions to be implemented at a later date. :)

static int lost_enclave()
{
	if (sgx_status == SGX_ERROR_ENCLAVE_LOST || sgx_status == SGX_ERROR_ENCLAVE_CRASHED) {
		launched = 0;
		return 1;
	}

	return 0;
}

ENCLAVEBRIDGE_API int ew_initialize()
{
	int vault_rv;

	if (!get_enclave(NULL)) return NL_STATUS_SGXERROR;

	sgx_status = ve_initialize(enclaveId, &vault_rv);
	// If our enclave crashed try loading it again.

	if (lost_enclave()) {
		if (!get_enclave(NULL)) return NL_STATUS_SGXERROR;
		sgx_status = ve_initialize(enclaveId, &vault_rv);
	}
	
	RETURN_SGXERROR_OR(vault_rv);
}

//read an existing vault

ENCLAVEBRIDGE_API int ew_initialize_from_header(const char *header, uint16_t hsize)
{
	int vault_rv;

	if (!get_enclave(NULL)) return NL_STATUS_SGXERROR;

	sgx_status = ve_initialize_from_header(enclaveId, &vault_rv, (unsigned char *)header, hsize);
	// If our enclave crashed try loading it again.
	if (lost_enclave()) {
		if (!create_enclave(NULL)) return NL_STATUS_SGXERROR;
		sgx_status = ve_initialize_from_header(enclaveId, &vault_rv, (unsigned char *)header, hsize);
	}

	RETURN_SGXERROR_OR(vault_rv);
}


ENCLAVEBRIDGE_API int ew_unlock(const char *password)
{
	int vault_rv;

	if (!get_enclave(NULL)) return NL_STATUS_SGXERROR;

	sgx_status = ve_unlock(enclaveId, &vault_rv, (char *) password);

	RETURN_SGXERROR_OR(vault_rv);
}



ENCLAVEBRIDGE_API int ew_lock()
{
	if (!get_enclave(NULL)) return NL_STATUS_SGXERROR;

	sgx_status = ve_lock(enclaveId);

	RETURN_SGXERROR_OR(NL_STATUS_OK);
}

ENCLAVEBRIDGE_API int ew_load_vault(const unsigned char *edata)
{
	int vault_rv;
	uint32_t dbsize;

	if (!get_enclave(NULL)) return NL_STATUS_SGXERROR;

	// We need to get the size of the password database before entering the enclave
	// to send the encrypted blob.

	sgx_status = ve_get_db_size(enclaveId, &dbsize);
	if (sgx_status == SGX_SUCCESS) {
		// Now we can send the encrypted vault data across.

		sgx_status = ve_load_vault(enclaveId, &vault_rv, (unsigned char *) edata, dbsize);
	}

	RETURN_SGXERROR_OR(vault_rv);
}

ENCLAVEBRIDGE_API int ew_get_db_size(uint32_t *size)
{
	if (!get_enclave(NULL)) return NL_STATUS_SGXERROR;

	sgx_status = ve_get_db_size(enclaveId, size);
	RETURN_SGXERROR_OR(NL_STATUS_OK);
}

ENCLAVEBRIDGE_API int ew_get_header(unsigned char *header, uint16_t *size)
{
	int vault_rv;

	if (!get_enclave(NULL)) return NL_STATUS_SGXERROR;

	if ( header == NULL ) sgx_status = ve_get_header_size(enclaveId, &vault_rv, size);
	else sgx_status = ve_get_header(enclaveId, &vault_rv, header, *size);

	RETURN_SGXERROR_OR(vault_rv);
}

ENCLAVEBRIDGE_API int ew_get_vault(unsigned char *edata, uint32_t *size)
{
	int vault_rv;

	if (!get_enclave(NULL)) return NL_STATUS_SGXERROR;

	if (edata == NULL) {
		sgx_status = ve_get_db_size(enclaveId, size);
		RETURN_SGXERROR_OR(NL_STATUS_OK);
	}

	sgx_status = ve_get_vault(enclaveId, &vault_rv, edata, *size);

	RETURN_SGXERROR_OR(vault_rv);
}

ENCLAVEBRIDGE_API int ew_set_master_password(const char *password)
{
	int vault_rv;

	if (!get_enclave(NULL)) return NL_STATUS_SGXERROR;

	sgx_status = ve_set_master_password(enclaveId, &vault_rv, (char *) password);

	RETURN_SGXERROR_OR(vault_rv);
}

ENCLAVEBRIDGE_API int ew_change_master_password(const char *oldpass, const char *newpass)
{
	int vault_rv;

	if (!get_enclave(NULL)) return NL_STATUS_SGXERROR;

	sgx_status = ve_change_master_password(enclaveId, &vault_rv, (char *)oldpass, (char *)newpass);

	RETURN_SGXERROR_OR(vault_rv);
}

ENCLAVEBRIDGE_API int ew_accounts_get_count(uint32_t *count)
{
	int vault_rv;

	if (!get_enclave(NULL)) return NL_STATUS_SGXERROR;

	sgx_status = ve_accounts_get_count(enclaveId, &vault_rv, count);

	RETURN_SGXERROR_OR(vault_rv);
}

ENCLAVEBRIDGE_API int ew_accounts_get_info_sizes(uint32_t idx, uint16_t *mbname_sz, uint16_t *mblogin_sz, uint16_t *mburl_sz)
{
	int vault_rv;

	if (!get_enclave(NULL)) return NL_STATUS_SGXERROR;

	sgx_status = ve_accounts_get_info_sizes(enclaveId, &vault_rv, idx, mbname_sz, mblogin_sz, mburl_sz);

	RETURN_SGXERROR_OR(vault_rv);
}

ENCLAVEBRIDGE_API int ew_accounts_get_info(uint32_t idx, char *mbname, uint16_t mbname_sz, char *mblogin, uint16_t mblogin_sz,
	char *mburl, uint16_t mburl_sz)
{
	int vault_rv;

	if (!get_enclave(NULL)) return NL_STATUS_SGXERROR;

	sgx_status = ve_accounts_get_info(enclaveId, &vault_rv, idx, mbname, mbname_sz, mblogin, mblogin_sz, mburl, mburl_sz);

	RETURN_SGXERROR_OR(vault_rv);
}

ENCLAVEBRIDGE_API int ew_accounts_get_password_size(uint32_t idx, uint16_t *sz)
{
	int vault_rv;

	if (!get_enclave(NULL)) return NL_STATUS_SGXERROR;

	sgx_status = ve_accounts_get_password_size(enclaveId, &vault_rv, idx, sz);
	RETURN_SGXERROR_OR(vault_rv);
}

ENCLAVEBRIDGE_API int ew_accounts_get_password(uint32_t idx, char *mbpass, uint16_t sz)
{
	int vault_rv;

	if (!get_enclave(NULL)) return NL_STATUS_SGXERROR;

	sgx_status = ve_accounts_get_password(enclaveId, &vault_rv, idx, mbpass, sz);

	RETURN_SGXERROR_OR(vault_rv);
}

ENCLAVEBRIDGE_API int ew_accounts_set_info(uint32_t idx, const char *mbname, uint16_t mbname_len, const char *mblogin, uint16_t mblogin_len,
	const char *mburl, uint16_t mburl_len)
{
	int vault_rv;

	if (!get_enclave(NULL)) return NL_STATUS_SGXERROR;

	sgx_status= ve_accounts_set_info(enclaveId, &vault_rv, idx, (char *)mbname, mbname_len, (char *)mblogin, mblogin_len, (char *)mburl, mburl_len);

	RETURN_SGXERROR_OR(vault_rv);
}

ENCLAVEBRIDGE_API int ew_accounts_set_password(uint32_t idx, const char *mbpass, uint16_t mbpass_len)
{
	int vault_rv;

	if (!get_enclave(NULL)) return NL_STATUS_SGXERROR;

	sgx_status = ve_accounts_set_password(enclaveId, &vault_rv, idx, (char *) mbpass, mbpass_len);

	RETURN_SGXERROR_OR(vault_rv);
}

ENCLAVEBRIDGE_API int ew_accounts_generate_password(uint16_t length, uint16_t pwflags, char *buffer)
{
	int vault_rv;

	if (!get_enclave(NULL)) return NL_STATUS_SGXERROR;

	sgx_status = ve_accounts_generate_password(enclaveId, &vault_rv, length, pwflags, buffer);

	RETURN_SGXERROR_OR(vault_rv);
}
